#ifndef LISTFIFO_STACK_H
#define LISTFIFO_STACK_H

#include "list.h"

#define LIST_FIFO_STACK_SUCCESS           LIST_SUCCESS
#define LIST_FIFO_STACK_ERR_NOMEM         LIST_ERR_NOMEM
#define LIST_FIFO_STACK_ERR_ARGVALUE      LIST_ERR_ARGVALUE
#define LIST_FIFO_STACK_ERR_CALLBACK      LIST_ERR_CALLBACK
#define LIST_FIFO_STACK_ERR_LEN0          LIST_ERR_LEN0
#define LIST_FIFO_STACK_ERR_UNKNOWN       LIST_ERR_UNKNOWN
#define LIST_FIFO_STACK_ERR_SET_CALLBACKS LIST_ERR_SET_CALLBACKS
#define LIST_FIFO_STACK_ERR_LEN_MAX       LIST_ERR_LEN_MAX
#define LIST_FIFO_STACK_ERR_INCONSISTENT  LIST_ERR_INCONSISTENT

typedef struct list_fifo_stack_s {
	//TRUE OR FLASE, if no error occurs, the last called function will set it to FALSE
	int has_error;
	int error_code;

	list_t *__list;
} list_fs_t;

typedef list_fs_t list_fifo_t;
typedef list_fs_t list_stack_t;

/*
 * get a listfifo/liststack
 * see list.h for defintion of default_callback
 * @default_callback:   default callback functions, NULL if not need
 *          the subsequence call to the list/stack who need set callback operation
 *          will check this first, if NOT null, will override the default
 *          callbacks(not really write it to the struct);
 */
list_fifo_t *list_fifo_new(data_op_t * default_callback);
list_stack_t *list_stack_new(data_op_t * default_callback);

list_fifo_t *list_fifo_new_with_copy(list_fifo_t * from, func_copy_t func_copy);
list_stack_t *list_stack_new_with_copy(list_stack_t * from,
				       func_copy_t func_copy);

/* error handler
 * get error string from error_code || get last error string
 */
const char *list_fifo_get_last_error_string(list_fifo_t * fifo);
const char *list_stack_get_last_error_string(list_stack_t * stack);
const char *list_fifo_get_error_string(const int error_code);
const char *list_stack_get_error_string(const int error_code);

/*
 * reset the callback functions
 * @return:  0 if no error
 */
int list_fifo_reset_callbacks(list_fifo_t * fifo, data_op_t * default_callback);
int list_stack_reset_callbacks(list_stack_t * stack,
			       data_op_t * default_callback);

/* return number of element in the listfifo/liststack
 * if error, return an nagetive number
 */
int list_fifo_size(list_fifo_t * fifo);
int list_stack_size(list_stack_t * stack);

/*
 * does the fifo/stack contains the data
 * @return: Postive Number indicates how many matches detected
 *          FALSE indicates that fifo/stack doesn't contain that data
 *          Nagetive Number indicates an error, and error has been set
 */
int list_fifo_contains(list_fifo_t * fifo, const void *data,
		       func_cmp_t func_cmp);
int list_stack_contains(list_stack_t * stack, const void *data,
			func_cmp_t func_cmp);

/* see list.h for defintion of func_copy_t
 * func_copy:   how to copy data field( do DIRECT ASSIGNMENT if NULL(if the default
 * callback setting is NULL too) )
 */
int list_fifo_push(list_fifo_t * fifo,
		   const void *data, const func_copy_t func_copy);
int list_stack_push(list_stack_t * stack,
		    const void *data, const func_copy_t func_copy);

/* @fifo/stack:   fifo/stack to operating on
 * @data:   data will stored where @data@ point to
 * @func_copy:   how to copy data field( do DIRECT ASSIGNMENT if NULL(if the default
 *              callback setting is NULL too) )  see list.h for defintion of func_free_t
 * @func_free:   how to delete data(NULL if not need to delete(if the default
 *              callback is NULL too, this will 
 *              do nothing to free the memory the data formly pushed))
 *              @return:  the @ data @
 */
void *list_fifo_pop(list_fifo_t * fifo,
		    void **data,
		    const func_copy_t func_copy, const func_free_t func_free);
void *list_stack_pop(list_stack_t * stack,
		     void **data,
		     const func_copy_t func_copy, const func_free_t func_free);

//return the data from the front of the fifo/stack
void *list_fifo_front(list_fifo_t * fifo);
void *list_stack_top(list_stack_t * stack);

/* @func_free:   how to delete data(NULL if not need to delete(if the default
 *              callback is NULL too))
 *              @return:  will set fifo/stack to NULL, and return NULL
 */
void *list_fifo_destroy(list_fifo_t ** fifo, const func_free_t func_free);
void *list_stack_destroy(list_stack_t ** stack, const func_free_t func_free);

/*
 * clear the stack/fifo
 * @func_free:    leave it NULL if needn't to free the data field(if the
 *                          default callback is NULL too)
 * @return: 0 if no error, else a Negative number is return , and err code been set
 */
int list_fifo_clear(list_fifo_t * fifo, const func_free_t func_free);
int list_stack_clear(list_stack_t * stack, const func_free_t func_free);

/* join the second stack/fifo to the rear of the front stack/fifo
 * @dst:  join to
 * @src:   join from
 * if func_copy or func_free is NULL, will use settings in dst's default_callbacks
 * @func_copy:  how to deal with the data 
 * @func_free:  how to destroy the data if the join failed 
 * @return:     joined stack/fifo, and error field is set correctly
 */
list_stack_t *list_stack_join(list_stack_t * dst, const list_stack_t * src,
			      func_copy_t func_copy, func_free_t func_free);
list_fifo_t *list_fifo_join(list_fifo_t * dst, const list_fifo_t * src,
			    func_copy_t func_copy, func_free_t func_free);

/* split original fifo/stack into two, after return ,the original fifo/stack will be set to NULL
 * @orig:  fifo/stack to be splited
 * @results:      an array[2] contains two pointers(need not allocate memory for their ref)
 * @num_in_first:   number of node will contains in the results[0]
 *
list_stack_t **list_stack_split(list_stack_t ** orig, list_stack_t * results[],
				const int num_in_first);
list_fifo_t **list_fifo_split(list_fifo_t ** orig, list_fifo_t * results[],
			      const int num_in_first);
 */

#endif
